package network

import (
	"context"
	"encoding/json"
	"github.com/golang/protobuf/proto"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/peer"
	"google.golang.org/grpc/status"
	"net/http"
	"pay/pkg/ecode"
	"pay/pkg/log"
	"pay/pkg/network/jsonpb"
	"pay/pkg/request_key"
)

// 自定义http请求json输出格式
func CustomJsonOutput() runtime.ServeMuxOption {
	return runtime.WithMarshalerOption(
		runtime.MIMEWildcard,
		&jsonpb.JSONPb{OrigName: true, EnumsAsInts: true, EmitDefaults: true})
}

// 自定义请求头
func CustomHeaderMatcher(customMap map[string]string) runtime.ServeMuxOption {
	return runtime.WithIncomingHeaderMatcher(func(key string) (string, bool) {
		if customMap != nil {
			if val, ok := customMap[key]; ok {
				return val, true
			}
		}
		if key == request_key.RequestIDKeyString {
			return key, true
		}
		return runtime.DefaultHeaderMatcher(key)
	})
}

// 自定义错误返回格式
func CustomProtoErrorHandler(errorDefaultProcessor func(s *status.Status, w http.ResponseWriter) (writeFinished bool),
	errorFormatter func(*ecode.ErrorCode) proto.Message) runtime.ServeMuxOption {
	return runtime.WithProtoErrorHandler(func(ctx context.Context, _ *runtime.ServeMux, m runtime.Marshaler,
		w http.ResponseWriter, r *http.Request, err error) {
		w.Header().Set("Content-type", m.ContentType())
		s := status.Convert(err)
		if s.Code() == codes.Unimplemented {
			w.WriteHeader(http.StatusNotFound)
			_, _ = w.Write([]byte(http.StatusText(http.StatusNotFound)))
			return
		}
		w.WriteHeader(http.StatusOK)
		if errorDefaultProcessor(s, w) {
			return
		}
		var remoteIP string
		if peerInfo, ok := peer.FromContext(ctx); ok {
			remoteIP = peerInfo.Addr.String()
		}
		// 直接落入这里，一般为其他情况引起的问题，比如request unmarshal失败，此时需要额外打印日志
		log.FromContext(ctx).Warnw("http",
			"status_code", "Fail-Unknown",
			"latency", 0,
			"client_ip", remoteIP,
			"method", r.Method,
			"path", r.URL.Path,
			"body", "unable to recover",
			"resp", "",
			"error", s.Message(),
			"stacktrace", s.Message(),
		)
		_ = json.NewEncoder(w).Encode(errorFormatter(ecode.BadRequest("参数格式错误")))
	})
}
